package uk.ac.ebi.dod.utils;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author Florian Reisinger
 * @author hturksoy
 * @since 0.1
 *
 * When the session is destroyed, we could register a sessionListener to preform clean-up operations.
 * (But for this simple use we don't have to)
 * Later, at the first request of the client, this filter will be fired and redirect
 * the user to the appropriate timeout page if the session is not valid.
 * (In cou case the timeout page is the welcome page)
 *
 *
 */
public class SessionTimeoutFilter implements Filter {

    private static final Log log = LogFactory.getLog(SessionTimeoutFilter.class);
    public static final String REQ_PARAM_SESSIONCONTROL_DISABLED = "sessionControlDisabled";
    public static final String REQ_PARAM_VALUE_SESSIONCONTROL_DISABLED = "true";


    private String timeoutPage = "pages/timeout.jsf";

    public void init(FilterConfig filterConfig) throws ServletException {
    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException,
            ServletException {

        if ((request instanceof HttpServletRequest) && (response instanceof HttpServletResponse)) {
            HttpServletRequest httpServletRequest = (HttpServletRequest) request;
            HttpServletResponse httpServletResponse = (HttpServletResponse) response;

            // is session expire control required for this request?
            if (isSessionControlRequiredForThisResource(httpServletRequest)) {

                if (!isSessionControlDisabled(httpServletRequest)) {

                    // is session invalid?
                    if (isSessionInvalid(httpServletRequest)) {
                        log.info("Session invalid/timed out! redirecting to timeout page.");
                        String timeoutUrl = httpServletRequest.getContextPath() + "/" + getTimeoutPage();
                        httpServletResponse.sendRedirect(timeoutUrl);
                        return;
                    }
                }
            }
        }
        filterChain.doFilter(request, response);
    }

    /**
     * The session shouldn't be checked for some pages. For example: the timeout page.
     * Since we're redirecting to the timeout page from this filter,
     * if we don't disable session control for it, the filter will again redirect to it
     * and this will result with an infinite loop...
     *
     * @param httpServletRequest the request that is to be check for session control.
     * @return true if session control mechnism should be applied to this resource.
     */
    private boolean isSessionControlRequiredForThisResource(HttpServletRequest httpServletRequest) {
        String requestPath = httpServletRequest.getRequestURI();
        boolean required = ( requestPath.indexOf(getTimeoutPage()) < 0 );
        if (log.isTraceEnabled()) {
            log.trace("Session control required for request path '" + requestPath + "'? " + required);
        }
        return required;
    }


    /*
    * Checks the request for specific parameter which is being used to disable session state.
    * This is used to skip the first load of the page, since the session is not yet valid then.
    */
    private boolean isSessionControlDisabled(HttpServletRequest httpServletRequest) {
        String disableSessionControlValue = httpServletRequest.getParameter(REQ_PARAM_SESSIONCONTROL_DISABLED);
        boolean scd = ( disableSessionControlValue != null && REQ_PARAM_VALUE_SESSIONCONTROL_DISABLED.equals(disableSessionControlValue) );
        if (log.isTraceEnabled()) {
            log.trace("Session control disabled for reqeust path '" + httpServletRequest.getRequestURI() + "'? " + scd);
        }
        return scd;
    }


    private boolean isSessionInvalid(HttpServletRequest httpServletRequest) {
        boolean sessionInvalid = (httpServletRequest.getRequestedSessionId() != null)
                && !httpServletRequest.isRequestedSessionIdValid();
        if (log.isTraceEnabled()) {
            log.trace("Current session is invalid.");
        }
        return sessionInvalid;
    }

    public void destroy() {
    }

    public String getTimeoutPage() {
        return timeoutPage;
    }

    public void setTimeoutPage(String timeoutPage) {
        this.timeoutPage = timeoutPage;
    }

}